/* $Id: MyApp.java 5361 2016-06-28 14:32:08Z nanang $ */
/*
 * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package com.cloudptt.api.product.pjsua2;

import android.content.Context;
import android.os.Environment;
import android.text.format.DateFormat;

import com.cloudptt.api.comm.CauseCont;
import com.cloudptt.api.pjsip.service.AppRTCAudioManager;
import com.cloudptt.api.product.Product;
import com.cloudptt.api.product.log.Log;
import com.cloudptt.api.pjsip.api.ImsProfile;

import org.pjsip.pjsua2.AccountConfig;
import org.pjsip.pjsua2.CodecInfo;
import org.pjsip.pjsua2.CodecInfoVector2;
import org.pjsip.pjsua2.CodecParam;
import org.pjsip.pjsua2.Endpoint;
import org.pjsip.pjsua2.EpConfig;
import org.pjsip.pjsua2.IpChangeParam;
import org.pjsip.pjsua2.LogConfig;
import org.pjsip.pjsua2.MediaConfig;
import org.pjsip.pjsua2.MediaFormatVideo;
import org.pjsip.pjsua2.StringVector;
import org.pjsip.pjsua2.TransportConfig;
import org.pjsip.pjsua2.UaConfig;
import org.pjsip.pjsua2.VidCodecParam;
import org.pjsip.pjsua2.pj_log_decoration;
import org.pjsip.pjsua2.pjmedia_echo_flag;
import org.pjsip.pjsua2.pjsip_transport_type_e;

import java.io.File;
import java.util.Date;

public class MyApp {
	public static String THIS_FILE = "MyApp";
	private final static String LogFilePath = Environment.getExternalStorageDirectory()
			+ File.separator + "CloudPTT" + File.separator + "logs"
			+ File.separator;
	public static Endpoint ep = null;
	private Context ctx = null;
	private final String configName = "pjsua2.json";
	private final int SIP_PORT = 5090;

	private final int LOG_LEVEL = 5;

	private MyAccount curAcc = null;

	private AppRTCAudioManager audioManager = null;

	//	private String appDir;
	//	private ArrayList<MyAccountConfig> accCfgs =
//			new ArrayList<MyAccountConfig>();
	private EpConfig epConfig = new EpConfig();
	private TransportConfig sipTpConfig = new TransportConfig();
	private boolean isInit = false;
	/* Maintain reference to log writer to avoid premature cleanup by GC */
	private MyLogWriter logWriter;

//	static {
//		try {
//			Log.d(THIS_FILE, "loadLibrary openh264 begin");
//			System.loadLibrary("openh264");
//			Log.d(THIS_FILE, "loadLibrary openh264 end");
////			Log.d(THIS_FILE, "loadLibrary opencore-amr begin");
////			System.loadLibrary("amr-codec");
////			Log.d(THIS_FILE, "loadLibrary opencore-amr end");
//		} catch (UnsatisfiedLinkError e) {
//			Log.d(THIS_FILE, "loadLibrary openh264 e = " + e.getMessage());
//			System.out.println("UnsatisfiedLinkError: " + e.getMessage());
//			System.out.println("This could be safely ignored if you " +
//					"don't need video.");
//		}
//		System.loadLibrary("pjsua2");
//		System.out.println("Library loaded");
//	}

	public MyApp(Context ctx)
	{
		Log.d(THIS_FILE, "MyApp begin");
		this.ctx = ctx;
//		loadLibrary();

		Log.d(THIS_FILE, "MyApp end");
	}


	public void init(String sbc,String dns)
	{
		init(false,sbc,dns);
	}

	public void init(boolean own_worker_thread,String sbc,String dns)
	{
		Log.d(THIS_FILE, "init  own_worker_thread = " + own_worker_thread + " sbc = " + sbc + " dns = " + dns);
		ep = new MyEndpoint();
		/* Create endpoint */
		try {
			ep.libCreate();
		} catch (Exception e) {
			return;
		}

		/* Override log level setting */

		/* Set log config. */
		LogConfig log_cfg = epConfig.getLogConfig();
		log_cfg.setLevel(LOG_LEVEL);
		log_cfg.setConsoleLevel(LOG_LEVEL);
		log_cfg.setFilename(getLogsFile());

		logWriter = new MyLogWriter();
		log_cfg.setWriter(logWriter);
		log_cfg.setDecor(log_cfg.getDecor() | pj_log_decoration.PJ_LOG_HAS_YEAR | pj_log_decoration.PJ_LOG_HAS_MONTH | pj_log_decoration.PJ_LOG_HAS_DAY_OF_MON);

		/* Write log to file (just uncomment whenever needed) */
//		String log_path = android.os.Environment.getExternalStorageDirectory().toString();
//		log_cfg.setFilename(log_path + File.separator + "CloudPTT" +
//		File.separator + "logs"
//				+ File.separator + "pttlog.txt");
//		LogConfig log_cfg = epConfig.getLogConfig();
//		log_cfg.setLevel(LOG_LEVEL);
//		log_cfg.setConsoleLevel(LOG_LEVEL);
////		log_cfg.setFilename(getLogsFile());
//
//		logWriter = new MyLogWriter();
//		log_cfg.setWriter(logWriter);
//		log_cfg.setDecor(log_cfg.getDecor() &
//				~(pj_log_decoration.PJ_LOG_HAS_CR |
//						pj_log_decoration.PJ_LOG_HAS_NEWLINE));

		/* Set ua config. */
		UaConfig ua_cfg = epConfig.getUaConfig();
		ua_cfg.setMwiUnsolicitedEnabled(false);
		String userAgent = Product.userAgent;
		Log.d(THIS_FILE, "init userAgent = " + userAgent);
		ua_cfg.setUserAgent(userAgent);
//		ua_cfg.getOutboundProxies().add(0,"sip:192.168.1.107:5060");
		ua_cfg.setStunIgnoreFailure(true);
		StringVector strVector = new StringVector();
		strVector.add(0,sbc);
		ua_cfg.setStunServer(strVector);


		Log.d(THIS_FILE, "init dns = " + dns);
		if(dns != null
				&& !dns.trim().equals("")
				&& !dns.equals("0.0.0.0"))
		{
			Log.d(THIS_FILE, "init Nameserver dns = " + dns);
			ua_cfg.getNameserver().clear();
			ua_cfg.getNameserver().add(0,dns);
		}
		else
		{
			Log.d(THIS_FILE, "init sbc = " + sbc);
			ua_cfg.getOutboundProxies().clear();
			ua_cfg.getOutboundProxies().add(0,"sip:" + sbc);
		}

		/* No worker thread */
		if (own_worker_thread) {
			ua_cfg.setThreadCnt(4);
			ua_cfg.setMainThreadOnly(true);
		}

		MediaConfig mediaConfig = epConfig.getMedConfig();
		mediaConfig.setPtime(20);
		epConfig.setMedConfig(mediaConfig);

		/* Init endpoint */
		try {
			ep.libInit(epConfig);
		} catch (Exception e) {
			return;
		}

		/* Create transports. */
		try {
			sipTpConfig.setPort(SIP_PORT);
			ep.transportCreate(pjsip_transport_type_e.PJSIP_TRANSPORT_UDP,
					sipTpConfig);
		} catch (Exception e) {
			System.out.println(e);
		}

		/* Set SIP port back to default for JSON saved config */
		sipTpConfig.setPort(SIP_PORT);

		Log.d(THIS_FILE, "set h264 begin");
		try {
			CodecInfoVector2 vector2 = ep.videoCodecEnum2();
			for(int i = 0; i< vector2.size(); i++)
			{
				CodecInfo info = vector2.get(i);
				Log.d(THIS_FILE, "CodecId = " + info.getCodecId());
				Log.d(THIS_FILE, "Desc = " + info.getDesc());
				Log.d(THIS_FILE, "info = " + info.toString());
			}
			Log.d(THIS_FILE, "set h264 all get begin");
			VidCodecParam param = ep.getVideoCodecParam("H264/99");
			Log.d(THIS_FILE, "set h264 all begin Width = " + param.getDecFmt().getWidth());
			Log.d(THIS_FILE, "set h264 all begin Height = " + param.getDecFmt().getHeight());
			Log.d(THIS_FILE, "set h264 all begin FpsNum = " + param.getDecFmt().getFpsNum());
			Log.d(THIS_FILE, "set h264 all begin AvgBps = " + param.getDecFmt().getAvgBps());
			Log.d(THIS_FILE, "set h264 all begin FpsDenum = " + param.getDecFmt().getFpsDenum());

			MediaFormatVideo decFmt = param.getDecFmt();
			Log.d(THIS_FILE, "set h264 Dec get end");
			decFmt.setHeight(640);
			decFmt.setWidth(480);
			decFmt.setFpsNum(20);
			Log.d(THIS_FILE, "set h264 Dec set begin");
			param.setDecFmt(decFmt);
			Log.d(THIS_FILE, "set h264 Dec set end");

			Log.d(THIS_FILE, "set h264 Enc get begin");
			MediaFormatVideo encFmt = param.getEncFmt();
			Log.d(THIS_FILE, "set h264 Enc get end");
			encFmt.setHeight(640);
			encFmt.setWidth(480);
			encFmt.setFpsNum(20);
			Log.d(THIS_FILE, "set h264 Enc set begin");
			param.setEncFmt(encFmt);
			Log.d(THIS_FILE, "set h264 Enc set end");

			ep.setVideoCodecParam("H264/99",param);

			param = ep.getVideoCodecParam("H264/99");
			Log.d(THIS_FILE, "set h264 all end Width = " + param.getDecFmt().getWidth());
			Log.d(THIS_FILE, "set h264 all end Height = " + param.getDecFmt().getHeight());
			Log.d(THIS_FILE, "set h264 all end FpsNum = " + param.getDecFmt().getFpsNum());
			Log.d(THIS_FILE, "set h264 all end AvgBps = " + param.getDecFmt().getAvgBps());
			Log.d(THIS_FILE, "set h264 all end FpsDenum = " + param.getDecFmt().getFpsDenum());

		} catch (Exception e) {
			e.printStackTrace();
			System.out.println(e);
			return;
		}
		Log.d(THIS_FILE, "set h264 end");

		/* Start. */
		try {
			Log.d(THIS_FILE, "libStart begin");
			ep.libStart();
			Log.d(THIS_FILE, "libStart end");
		} catch (Exception e) {
			e.printStackTrace();
			return;
		}

		initAudioManager();
	}

	public void uninit()
	{
		Log.d(THIS_FILE, "uninit " );
		/* Start. */
		try {
			Log.d(THIS_FILE, "libDestroy begin");
			ep.libStopWorkerThreads();
			ep.libDestroy();
			ep.delete();
			Log.d(THIS_FILE, "libDestroy end");
		} catch (Exception e) {
			e.printStackTrace();
			return;
		}
		ep = null;
		isInit = false;
	}

	public static String getLogsFile() {
		Date d = new Date();
		StringBuffer fileName = new StringBuffer();
		fileName.append(LogFilePath);
		fileName.append("pjsip_");
		fileName.append(DateFormat.format("yyMMddkkmmss", d));
		fileName.append(".txt");
		return fileName.toString();
	}
	public boolean addCurAcc(ImsProfile imsProfile,String pwd)
	{
		Log.d(THIS_FILE, "addAcc isInit = " + isInit + " imsProfile = " + imsProfile);
		try {
			if(!isInit) {
				String sbc = imsProfile.getSbc();
				String dns = imsProfile.getDns();
				init(sbc, dns);
				isInit = true;
			}

//			if(curAcc != null)
//			{
//				curAcc.setRegistration(false);
//				curAcc.delete();
//				curAcc = null;
//			}
			if(curAcc == null) {
				curAcc = new MyAccount(ctx,imsProfile,pwd);
				curAcc.create();
			}
			else
			{
				curAcc.updateAccCfg(imsProfile,pwd);
				curAcc.modify();
			}

//			UaConfig ua_cfg = epConfig.getUaConfig();
//			ua_cfg.setUserAgent("Pjsua2 Android " + ep.libVersion().getFull());
//			StringVector strVec = new StringVector();
//			strVec.add(0,imsProfile.getDomain());
//			ua_cfg.setNameserver(strVec);
//			epConfig.setUaConfig(ua_cfg);

			curAcc.setRegistration(true);
		}
		catch (Exception e)
		{
			e.printStackTrace();
			return false;
		}

		return true;
	}

	public boolean delCurAcc()
	{
		Log.d(THIS_FILE, "delCurAcc");
		try {
			if(curAcc != null) {
				curAcc.setRegistration(false);
				curAcc.clean();
//				curAcc.delete();
//				curAcc = null;
//				uninit();
			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
			return  false;
		}
		return true;
//		accList.remove(acc);
	}

	public boolean updateCurAcc()
	{
		Log.d(THIS_FILE, "delAcc");
		try {
			curAcc.setRegistration(true);
		}
		catch (Exception e)
		{
			e.printStackTrace();
			return  false;
		}
		return true;
	}

	public void setCaptureOrient(int orient)
	{
		Log.d(THIS_FILE, "setCaptureOrient orient = " + orient);
		if (MyApp.ep != null && curAcc != null) {
			try {
				AccountConfig cfg = curAcc.accCfg;//.cfg;
				int cap_dev = cfg.getVideoConfig().getDefaultCaptureDevice();
				MyApp.ep.vidDevManager().setCaptureOrient(cap_dev, orient,
						true);
			} catch (Exception e) {
				e.printStackTrace();
				Log.d(THIS_FILE, "setCaptureOrient Exception = " + e);
			}
		}
	}

//	private void loadConfig(String filename)
//	{
//		Log.d(THIS_FILE, "loadConfig filename = " + filename);
//		JsonDocument json = new JsonDocument();
//
//		try {
//			/* Load file */
//			json.loadFile(filename);
//			ContainerNode root = json.getRootContainer();
//
//			/* Read endpoint config */
//			epConfig.readObject(root);
//
//			/* Read transport config */
//			ContainerNode tp_node = root.readContainer("SipTransport");
//			sipTpConfig.readObject(tp_node);
//
//			/* Read account configs */
////			accCfgs.clear();
////			ContainerNode accs_node = root.readArray("accounts");
////			while (accs_node.hasUnread()) {
////				MyAccountConfig acc_cfg = new MyAccountConfig();
////				acc_cfg.readObject(accs_node);
////				accCfgs.add(acc_cfg);
////			}
//		} catch (Exception e) {
//			System.out.println(e);
//		}
//
//		/* Force delete json now, as I found that Java somehow destroys it
//		 * after lib has been destroyed and from non-registered thread.
//		 */
//		json.delete();
//	}

//	private void buildAccConfigs()
//	{
//		/* Sync accCfgs from accList */
//		accCfgs.clear();
//		for (int i = 0; i < accList.size(); i++) {
//			MyAccount acc = accList.get(i);
//			MyAccountConfig my_acc_cfg = new MyAccountConfig();
//			my_acc_cfg.accCfg = acc.cfg;
//
//			my_acc_cfg.buddyCfgs.clear();
//			for (int j = 0; j < acc.buddyList.size(); j++) {
//				MyBuddy bud = acc.buddyList.get(j);
//				my_acc_cfg.buddyCfgs.add(bud.cfg);
//			}
//			Log.d(THIS_FILE, "buildAccConfigs my_acc_cfg = " + my_acc_cfg);
//			accCfgs.add(my_acc_cfg);
//		}
//	}

//	private void saveConfig(String filename)
//	{
//		JsonDocument json = new JsonDocument();
//
//		try {
//			/* Write endpoint config */
//			json.writeObject(epConfig);
//
//			/* Write transport config */
//			ContainerNode tp_node = json.writeNewContainer("SipTransport");
//			sipTpConfig.writeObject(tp_node);
//
//			/* Write account configs */
//			buildAccConfigs();
//			ContainerNode accs_node = json.writeNewArray("accounts");
//			for (int i = 0; i < accCfgs.size(); i++) {
//				accCfgs.get(i).writeObject(accs_node);
//			}
//
//			/* Save file */
//			json.saveFile(filename);
//		} catch (Exception e) {}
//
//		/* Force delete json now, as I found that Java somehow destroys it
//		 * after lib has been destroyed and from non-registered thread.
//		 */
//		json.delete();
//	}

	public void handleNetworkChange()
	{
		try{
			Log.d(THIS_FILE, "handleNetworkChange ep = " + ep);
			if(ep != null) {
				IpChangeParam changeParam = new IpChangeParam();
				ep.handleIpChange(changeParam);
				updateCurAcc();
			}
		} catch (Exception e) {
			System.out.println(e);
		}
	}

	public void deinit()
	{
//		String configPath = appDir + "/" + configName;
//		saveConfig(configPath);

		uninitAudioManager();

		/* Try force GC to avoid late destroy of PJ objects as they should be
		 * deleted before lib is destroyed.
		 */
		Runtime.getRuntime().gc();

		/* Shutdown pjsua. Note that Endpoint destructor will also invoke
		 * libDestroy(), so this will be a test of double libDestroy().
		 */
		try {
			ep.libDestroy();
		} catch (Exception e) {}

		/* Force delete Endpoint here, to avoid deletion from a non-
		 * registered thread (by GC?).
		 */
		ep.delete();
		ep = null;
	}

	private void initAudioManager()
	{
		if(audioManager == null)
		{
			audioManager = AppRTCAudioManager.create(ctx);
			audioManager.init();
		}
	}
	private void uninitAudioManager()
	{
		if(audioManager != null)
		{
			audioManager.close();
			audioManager = null;
		}
	}

	public AppRTCAudioManager getAudioManager() {
		return audioManager;
	}

	public int checkSystemErr()
	{
		if(null == curAcc)
		{
			return CauseCont.OperateCheck_Sys_AccountNull;
		}

		return curAcc.checkSystemErr();
	}

	public int checkCall(String num)
	{
		Log.e(THIS_FILE, "checkIsMySelf num is " + num);
		if(num == null
				|| num.equals(""))
		{
			return CauseCont.OperateCheck_Call_NumNull;
		}
		return CauseCont.OperateCheck_Suc;
	}

	public MyAccount getCurAcc() {
		return curAcc;
	}
}
